package IC;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

import java_cup.runtime.Symbol;
import IC.AST.ASTNode;
import IC.AST.ICClass;
import IC.AST.Program;
import IC.AST.Visitors.PrettyPrinter;
import IC.Parser.Lexer;
import IC.Parser.LibraryParser;
import IC.Parser.Parser;
import IC.Parser.ParserMonitor;
/**
 * 
 * Main Class for second ex, Compilation course fall 09-10.
 * Usage: java IC.Compiler <file.ic> [-L</path/to/libic.sig>] [-print-ast]
 * 
 * @since 2009-11-20
 *
 */
public class Compiler {

	/* Preset parameters for printing of parser messages */
	public static final boolean printRuleReductions = false;
	public static final boolean printTokens = true;
	
	/* these are all the user output msgs from the program */	
	public static final String MSG__USAGE 					= "Usage: java IC.Compiler <file.ic> [-L</path/to/libic.sig>] [-print-ast]";
	public static final String MSG__ERROR_PARSING_FILE 		= "Error parsing file: ";
	public static final String MSG__ERROR_PARSING_LIB_FILE 	= "Error parsing library file: ";
	public static final String MSG__FILE_NOT_FOUND 			= "File not found: ";
	public static final String MSG__LIB_FILE_NOT_FOUND 		= "Library file not found: ";
	public static final String MSG__PARSED		 			= "\r\nParsed ";
	public static final String MSG__PARSED_LIB	 			= "\r\nParsed library ";
	public static final String MSG__FINISHED_FILE 			= " successfuly!";
	public static final String MSG__FINISHED_LIB_FILE 		= " successfuly!";
	public static final String MSG__ERRORS_FOUND_IN_FILE 	= "The following errors were found in the file: ";
	

	/* name of print-arg of usage */
	private static final String PRINT_AST_FLAG = "-print-ast";
	
	/* This can be set using command line argument PRINT_AST_FLAG, this is the default value */
	public static boolean activatePrettyPrinter = false;

	public static void main(String[] args) {

		String libraryFileName = null;

		// checking and parsing arguments
		try {
			// must have args
			if (args.length == 0) {
				throw new Exception();
			}

			// checking that all the args are legal
			for (int i = 1; i < args.length; i++) {
				String arg = args[i];
				// arg must either be this one
				if (arg.equals(PRINT_AST_FLAG))
					activatePrettyPrinter = true;
				// or that one
				else {
					if ((libraryFileName = extractLibFilenameFromArg(arg)) == null) {
						// its none of the legal args - exit!
						throw new Exception();
					}
				}
			}
		} catch (Exception e1) {
			System.err
					.println(MSG__USAGE);
			return;
		}

		// Parsing main file 
		try {
			FileInputStream fis = new FileInputStream(args[0]);
			Program AST = parseFile(fis);
			// pretty printer will activate only if (activatePrettyPrinter==true)
			System.out.println(MSG__PARSED + args[0] + MSG__FINISHED_FILE); 
			if (AST == null)
				throw new Exception();
			doPrettyPrint(args[0], AST);
		} catch (FileNotFoundException e) {
			System.err.println(MSG__FILE_NOT_FOUND + args[0]);
			return;
		} catch (Exception e) {
			// thrown in case parser did not work correctly
			System.err.println(MSG__ERROR_PARSING_FILE + args[0]);
			return;
		}
			
//	parsing library file if supplied
		if (libraryFileName != null) {
			try {
				FileInputStream libfis = new FileInputStream(libraryFileName);
				ICClass libraryAST = parseLibraryFile(libfis);
				System.out.println(MSG__PARSED_LIB +  libraryFileName + MSG__FINISHED_LIB_FILE);
				if (libraryAST == null)
					throw new Exception();
				// pretty printer will activate only argument supplied by user
				doPrettyPrint(libraryFileName, libraryAST);
			} catch (FileNotFoundException e) {
				System.err
						.println(MSG__LIB_FILE_NOT_FOUND + libraryFileName);
				return;
			} catch (Exception e) {
				// thrown in case parser did not work correctly
				System.err.println(MSG__ERROR_PARSING_LIB_FILE	+ libraryFileName);
				return;
			}
		}
	}

	/**
	 * If activatePrettyPrinter is set above, this will print the supplied AST
	 * 
	 * @param libraryFileName
	 * @param libraryAST
	 */
	private static void doPrettyPrint(String libraryFileName, ASTNode libraryAST) {
		if (activatePrettyPrinter) {
			PrettyPrinter printer = new PrettyPrinter(libraryFileName);
			System.out.println(libraryAST.accept(printer));
		}
	}

	/**
	 * <i>Parse main code file</i>
	 * 
	 * @param fis
	 *            input stream of the code file
	 * @return Program main node if success, or if error recovery is set and
	 *         recovery was possible. o/w null.
	 * @throws Exception
	 */
	private static Program parseFile(FileInputStream fis) {
		Symbol parseSymbol = null;
		try {
			Lexer lex = new Lexer(fis);
			Parser parser = new Parser(lex);
			// will print tokens from lexer to System.out, iff
			// setPrintRuleReductions(true).
			parser.setPrintTokens(printTokens);
			// will inform after every rule reduction to System.out.
			parser.setPrintRuleReductions(printRuleReductions);
			parseSymbol = parser.parse();
		} catch (Exception e) {
		}	
		/*
		 * If errors occured in parser, print them and exit; can also choose to
		 * ignore them (since they were fixed by the parser);
		 */
		if (ParserMonitor.hasErrors()) {
			System.err.println(MSG__ERRORS_FOUND_IN_FILE);
			for (String err : ParserMonitor.getErrors())
				System.err.println(err);
			return null;
		}		
		
		Program root = (Program) parseSymbol.value;

		return root;
	}

	/**
	 * <i>Parse library code file</i>
	 * 
	 * @param fis
	 *            input stream of the code file
	 * @return Program main node if success, or if error recovery is set and
	 *         recovery was possible. o/w null.
	 * @throws Exception
	 */
	private static ICClass parseLibraryFile(FileInputStream fis) {
		Symbol parseSymbol = null;
		try {
			Lexer lex = new Lexer(fis);
			LibraryParser parser = new LibraryParser(lex);
			// will print tokens from lexer to System.out, iff
			parser.setPrintTokens(printTokens);
			// will inform after every rule reduction to System.out.
			parser.setPrintRuleReductions(printRuleReductions);
			parseSymbol = parser.parse();
		} catch (Exception e) {
			
		}
		/*
		 * If errors occured in parser, print them and exit; can also choose to
		 * ignore them (since they were fixed by the parser);
		 */
		if (ParserMonitor.hasErrors()) {
			System.err.println(MSG__ERRORS_FOUND_IN_FILE);
			for (String err : ParserMonitor.getErrors())
				System.err.println(err);
			return null;
		}
		ICClass root = (ICClass) parseSymbol.value;

		return root;
	}

	/**
	 * removes the leading -L from the filename argument
	 * 
	 * @param arg
	 * @return name of lib file if arg starts with -L, o/w null
	 */
	private static String extractLibFilenameFromArg(String arg) {
		if (!arg.substring(0, 2).equals("-L"))
			return null;
		return arg.substring(2);
	}
}
